package com.fouram.flowable.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.fouram.flowable.constants.FlowConstants;
import com.fouram.flowable.domain.bo.*;
import com.fouram.flowable.domain.vo.HistoricActivityInstanceVo;
import com.fouram.flowable.domain.vo.TaskVo;
import com.fouram.flowable.service.WorkFlowService;
import com.google.common.collect.Lists;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.DeploymentBuilder;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Comment;
import org.flowable.image.ProcessDiagramGenerator;
import org.flowable.task.api.Task;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

@Service
@RequiredArgsConstructor
public class WorkFlowServiceImpl implements WorkFlowService {

    private final TaskService taskService;
    private final ProcessEngine processEngine;
    private final RuntimeService runtimeService;
    private final HistoryService historyService;
    private final RepositoryService repositoryService;

    @Override
    public ProcessDefinition deployDeployment(DeployDeploymentBo bo) {
        DeploymentBuilder builder = repositoryService.createDeployment();
        Deployment deployment = builder.addInputStream(bo.getFileName(), bo.getInputStream())
                .name(bo.getProcessName()).key(bo.getProcessKey()).deploy();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .deploymentId(deployment.getId())
                .singleResult();
        return processDefinition;
    }

    @Override
    public ProcessInstance startProcessInstance(StartProcessInstanceBo bo){
        return runtimeService.startProcessInstanceById(bo.getProcessDefinitionId(), bo.getVariables());
    }

    private String getDefinitionIdByInstanceId(String processInstanceId){
        String processDefinitionId;
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        HistoricProcessInstance hpi = null;
        // 如果流程已经结束，则得到结束节点
        if (processInstance == null) {
            hpi = historyService.createHistoricProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
            processDefinitionId = hpi.getProcessDefinitionId();
        } else {
            // 如果流程没有结束，则取当前活动节点
            // 根据流程实例ID获得当前处于活动状态的ActivityId合集
            processDefinitionId = processInstance.getProcessDefinitionId();
        }
        return processDefinitionId;
    }

    @Override
    public void getHistoryImage(GetHistoryImageBo bo) throws Exception {
        HttpServletResponse response = bo.getResponse();
        String processInstanceId = bo.getProcessInstanceId();
        String processDefinitionId = getDefinitionIdByInstanceId(processInstanceId);
        List<String> highLightedActivities = new ArrayList<>();
        // 获得活动的节点
        List<HistoricActivityInstance> highLightedActivityList = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc().list();
        List<String> highLightedFlows = new ArrayList<>();
        for (HistoricActivityInstance tempActivity : highLightedActivityList) {
            String activityId = tempActivity.getActivityId();
            highLightedActivities.add(activityId);
            if(FlowConstants.SEQUENCE_FLOW.equals(tempActivity.getActivityType())){
                highLightedFlows.add(tempActivity.getActivityId());
            }
        }
        // 获取流程图
        BpmnModel bpmnModel = repositoryService.getBpmnModel(processDefinitionId);
        ProcessEngineConfiguration engConf = processEngine.getProcessEngineConfiguration();
        ProcessDiagramGenerator diagramGenerator = engConf.getProcessDiagramGenerator();
        byte[] buf = new byte[1024];
        int length;
        try (InputStream in = diagramGenerator.generateDiagram(bpmnModel, "png", highLightedActivities,
                highLightedFlows, engConf.getActivityFontName(),
                engConf.getLabelFontName(), engConf.getAnnotationFontName(), engConf.getClassLoader(),
                1.0, true);
             OutputStream out = response.getOutputStream()) {
            while ((length = in.read(buf)) != -1) {
                out.write(buf, 0, length);
            }
        }
    }

    @Override
    public List<HistoricActivityInstanceVo> getHistoryList(GetHistoryListBo bo) {
        String processInstanceId = bo.getProcessInstanceId();
        List<HistoricActivityInstance> activityInstances = historyService.createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId).orderByHistoricActivityInstanceStartTime().asc()
                .orderByHistoricActivityInstanceEndTime().asc().list();
        List<HistoricActivityInstanceVo> list = new ArrayList<>();
        for(HistoricActivityInstance hai : activityInstances){
            if(!StrUtil.containsAny(hai.getActivityType(), FlowConstants.USER_TASK,
                    FlowConstants.START_EVENT, FlowConstants.END_EVENT)){
                continue;
            }
            HistoricActivityInstanceVo vo = new HistoricActivityInstanceVo();
            vo.setStartTime(hai.getStartTime());
            vo.setEndTime(hai.getEndTime());
            vo.setAssignee(hai.getAssignee());
            if(FlowConstants.START_EVENT.equals(hai.getActivityType())){
                vo.setActivityName("开始");
                vo.setComment("提交");
            } else if(FlowConstants.END_EVENT.equals(hai.getActivityType())){
                vo.setActivityName("结束");
                vo.setComment("完成");
            } else {
                vo.setActivityName(hai.getActivityName());
                List<Comment> commentList = taskService.getTaskComments(hai.getTaskId());
                if(CollUtil.isNotEmpty(commentList)){
                    vo.setComment(commentList.get(0).getFullMessage());
                }
            }
            list.add(vo);
        }
        return list;
    }

    @Override
    public List<TaskVo> getTodoTaskList(GetTodoTaskListBo bo){
        String assignee = bo.getAssignee();
        int pageNo = bo.getPageNo();
        int pageSize = bo.getPageSize();
        List<Task> taskList = taskService.createTaskQuery().taskAssignee(assignee).orderByTaskId().desc()
                .listPage((pageNo - 1) * pageSize, pageSize);
        List<TaskVo> tasks = Lists.newArrayList();
        if (CollectionUtils.isNotEmpty(taskList)) {
            for (Task task : taskList) {
                TaskVo taskVo = new TaskVo();
                taskVo.setTaskId(task.getId());
                taskVo.setTaskName(task.getName());
                taskVo.setProcessInstanceId(task.getProcessInstanceId());
                tasks.add(taskVo);
            }
        }
        return tasks;
    }

    @Override
    public void completeTask(CompleteTaskBo bo){
        taskService.complete(bo.getTaskId(), bo.getVariables());
        if (StrUtil.isNotBlank(bo.getComment())) {
            Task task = taskService.createTaskQuery().taskId(bo.getTaskId()).singleResult();
            taskService.addComment(bo.getTaskId(), task.getProcessInstanceId(), bo.getComment());
        }
    }

    @Override
    public void changeTaskAssignee(ChangeTaskAssigneeBo bo){
        taskService.setAssignee(bo.getTaskId(), bo.getAssignee());
    }
}

